﻿using System;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using WatiN.Core;

namespace Consult
{
    public enum TypeOfBrowser
    {
        IE,
        FF
        //Chrome
    }

    public enum TypeOfParser
    {
        SchengenVisa,
        NationalVisa,
        Karta
    }

    public class CaptchaEventArgs : EventArgs
    {
        public System.Drawing.Image Image { get; set; }
        public string Result { get; set; }
    }

    public class MessageEventArgs : EventArgs
    {
        public string Message { get; set; }
        public bool IsTerminate { get; set; }
    }

    public class ConsultWorker : IDisposable
    {
        private const bool IsInDebug = false;

        private IE _ie;
        private FireFox _ff;
        //private Chrome _ch;
        private bool _isStopped = true;

        private int _timeout = 60;
        //private const string ConsultSchengenVisaUrl = "https://secure.e-konsulat.gov.pl/Uslugi/RejestracjaTerminu.aspx?IDUSLUGI=8";
        private const string ConsultSchengenVisaUrl = "https://secure.e-konsulat.gov.pl/default.aspx";
        //private const string ConsultKartaUrl = "https://secure.e-konsulat.gov.pl/Uslugi/RejestracjaTerminu.aspx?IDUSLUGI=7";
        private const string ConsultKartaUrl = "https://secure.e-konsulat.gov.pl/default.aspx";
        //private const string ConsultNationalVisaUrl = "https://secure.e-konsulat.gov.pl/Uslugi/RejestracjaTerminu.aspx?IDUSLUGI=1";
        private const string ConsultNationalVisaUrl = "https://secure.e-konsulat.gov.pl/default.aspx";

        private const string LinkNationalVisaId = "ctl00_ctl24_hlRejestracjaKrajowa";
        private const string LinkKartaId = "ctl00_ctl24_hlRejestracjaKrajowa";
        private const string LinkSchengenId = "ctl00_ctl24_hlRejestracjaSchengen";
        private const int LanguageId = 17;
        private const string LanguageComboId = "ctl00_ddlWersjeJezykowe";
        private const string LanguageChangeButton = "ctl00_ibWersjeJezykowe";
        private const string CaptchaImageId = "ctl00_ContentPlaceHolder1_KomponentObrazkowy_CaptchaImageID";
        private const string CaptchaTextId = "ctl00_ContentPlaceHolder1_KomponentObrazkowy_VerificationID";
        private const string NextButtonId = "ctl00_ContentPlaceHolder1_btnDalej";
        private const string CountryComboId = "ctl00_tresc_cbListaKrajow";
        //private const string CountryComboId = "ctl00_ContentPlaceHolder1_cbListaKrajow";
        private const string CityComboId = "ctl00_tresc_cbListaPlacowek";
        //private const string CityComboId = "ctl00_ContentPlaceHolder1_cbListaPlacowek";
        private const string TypeComboId = "ctl00_ContentPlaceHolder1_cbRodzajUslugi";
        private const string OfficeComboId = "ctl00_ContentPlaceHolder1_cbLokalizacja";
        private const string TimeComboId = "ctl00_ContentPlaceHolder1_cbTermin";
        private const string HourComboId = "ctl00_ContentPlaceHolder1_cbGodzina";
        private const string ErrorLabelId = "ctl00_ContentPlaceHolder1_lblBrakTerminow";
        private const string ReserveButtonId = "ctl00_ContentPlaceHolder1_btnRezerwuj";

        public event EventHandler<CaptchaEventArgs> NeedCapthaRecognize;
        public event EventHandler<MessageEventArgs> SomeMessage;
        public event EventHandler<MessageEventArgs> SomeLog;

        public TypeOfBrowser TypeOfBrowser { get; set; }
        public string CountryName { get; set; }
        public string CityName { get; set; }
        public string TypeName { get; set; }
        public string OfficeName { get; set; }

        public TypeOfParser TypeOfParser { get; set; }

        public string ConsultUrl
        {
            get
            {
                switch (TypeOfParser)
                {
                    case TypeOfParser.Karta:
                        return ConsultKartaUrl;
                    case TypeOfParser.NationalVisa:
                        return ConsultNationalVisaUrl;
                    case TypeOfParser.SchengenVisa:
                    default:
                        return ConsultSchengenVisaUrl;
                }
            }
        }

        public string LinkId
        {
            get
            {
                switch (TypeOfParser)
                {
                    case TypeOfParser.Karta:
                        return LinkKartaId;
                    case TypeOfParser.NationalVisa:
                        return LinkNationalVisaId;
                    case TypeOfParser.SchengenVisa:
                    default:
                        return LinkSchengenId;
                }
            }
        }


        public int Timeout
        {
            get
            {
                return _timeout;
            }
            set
            {
                _timeout = value;
                ReinitTimeout();
            }
        }

        private void ReinitTimeout()
        {
            Settings.WaitForCompleteTimeOut = _timeout;
            Settings.AttachToBrowserTimeOut = _timeout;
            Settings.WaitUntilExistsTimeOut = _timeout;
        }

        private Browser Browser
        {
            get
            {
                if (!ProveTermination())
                    return null;
                var allNulls =
                    (_ff == null &&
                    //_ch == null &&
                     _ie == null);
                if (allNulls)
                {
                    ReinitTimeout();
                    allNulls = false;
                }
                switch (TypeOfBrowser)
                {
                    case TypeOfBrowser.IE:
                        CloseBrowser(_ff);
                        //CloseBrowser(_ch);
                        if (_ie == null)
                        {
                            //_ie = new IE { Visible = false, AutoClose = true };
                            _ie = new IE { Visible = IsInDebug };
                            //_ie = new IE();
                            allNulls = true;
                        }
                        break;
                    case TypeOfBrowser.FF:
                        CloseBrowser(_ie);
                        //CloseBrowser(_ch);
                        if (_ff == null)
                        {
                            _ff = new FireFox();
                            allNulls = true;
                        }
                        break;
                    //case TypeOfBrowser.Chrome:
                    //    CloseBrowser(_ff);
                    //    CloseBrowser(_ie);
                    //    if (_ch == null)
                    //    {
                    //        _ch = new Chrome { Visible = true, AutoClose = true };
                    //        allNulls = true;
                    //    }
                    //    break;
                }

                if (allNulls)
                {
                    ReinitTimeout();
                    Settings.AutoCloseDialogs = true;
                }
                switch (TypeOfBrowser)
                {
                    case TypeOfBrowser.IE:
                        return _ie;
                    case TypeOfBrowser.FF:
                        return _ff;
                    //case TypeOfBrowser.Chrome:
                    //return _ch;
                }
                return null;
            }
        }

        private static void CloseBrowser(Browser item)
        {
            if (item == null) return;
            item.Close();
            item = null;
        }

        public void RunThread()
        {
            try
            {
                Start();
            }
            catch (Exception ex)
            {
                if (SomeMessage != null)
                    SomeMessage(this, new MessageEventArgs { Message = Consult.ConsultWorker_RunThread_Catching_exception__ + ex.Message });
            }
        }

        public bool Start()
        {
            _isStopped = false;
            try
            {
                Browser.GoTo(ConsultUrl);
            }
            catch
            {
            }
            if (!ProveOnLanguage())
                return false;

            //if (!ProveOnCaptcha())
                //return false;

            if (!InitCountry())
                return false;

            if (!InitCity())
                return false;

            if (!InitLink())
                return false;
            //if (!InitType())
            //    return false;

            if (!RunLifeCycle())
                return false;

            if (!ProveTermination())
                return false;

            if (Browser is IE)
                (Browser as IE).Visible = true;
            return true;
        }

        private bool RunLifeCycle()
        {
            var selectedDate = string.Empty;
            while (true)
            {
                Browser.Refresh();

                if (!ProveTermination())
                    return false;

                if (!ProveOnCaptcha())
                    return false;

                //if (!InitOffice(true))
                //    return false;

                //if (!ProveTermination())
                //    return false;

                var dates = Browser.SelectList(Find.ById(TimeComboId));
                if (!dates.Check())
                {
                    if (SomeMessage != null)
                    {
                        var args = new MessageEventArgs
                                       {
                                           Message =
                                               Consult.ConsultWorker_RunLifeCycle_Control_with_dates_does_not_found
                                       };
                        SomeMessage(this, args);
                        if (args.IsTerminate)
                        {
                            _isStopped = true;
                        }
                    }
                    return false;
                }
                if (!ProveTermination())
                    return false;

                if (dates.AllContents.Count <= 1)
                {
                    if (SomeLog != null)
                    {
                        var error = Browser.Span(Find.ById(ErrorLabelId));
                        var args = new MessageEventArgs
                        {
                            Message = error.Check() ? error.Text : Consult.ConsultWorker_RunLifeCycle_There_are_no_empty_dates
                        };
                        SomeLog(this, args);
                    }
                    continue;
                }
                selectedDate = dates.Options.ElementAt(1).Text;
                dates.Select(selectedDate);
                Browser.WaitForAsyncPostBackToComplete();

                if (!ProveTermination())
                    return false;

                var selectedTime = string.Empty;

                switch (TypeOfParser)
                {

                    case TypeOfParser.Karta:
                        //var times = Browser.SelectList(Find.ById(HourComboId));
                        //if (!times.Check())
                        //{
                        //    if (SomeMessage != null)
                        //    {
                        //        var args = new MessageEventArgs
                        //                       {
                        //                           Message =
                        //                               Consult.ConsultWorker_RunLifeCycle_Control_with_times_does_not_found
                        //                       };
                        //        SomeMessage(this, args);
                        //        if (args.IsTerminate)
                        //        {
                        //            _isStopped = true;
                        //        }
                        //    }
                        //    return false;
                        //}
                        //if (!ProveTermination())
                        //    return false;

                        //if (times.AllContents.Count <= 1)
                        //{
                        //    if (SomeLog != null)
                        //    {
                        //        var error = Browser.Span(Find.ById(ErrorLabelId));
                        //        var args = new MessageEventArgs
                        //        {
                        //            Message = error.Check() ? error.Text : Consult.ConsultWorker_RunLifeCycle_There_are_no_empty_times
                        //        };
                        //        SomeLog(this, args);
                        //    }
                        //    continue;
                        //}
                        //selectedTime = times.Options.ElementAt(1).Text;
                        //times.Select(selectedTime);
                        //Browser.WaitForAsyncPostBackToComplete();

                        //if (!ProveTermination())
                        //    return false;
                        break;
                }


                var next = Browser.Button(Find.ById(ReserveButtonId));
                if (!next.Check() || !next.Enabled)
                {
                    if (SomeLog != null)
                    {
                        var error = Browser.Span(Find.ById(ErrorLabelId));
                        var args = new MessageEventArgs
                        {
                            Message = error.Check() ? error.Text : Consult.ConsultWorker_RunLifeCycle_There_are_no_empty_dates
                        };
                        SomeLog(this, args);
                    }
                    continue;
                }
                next.Click();
                Browser.WaitForAsyncPostBackToComplete();

                if (!ProveTermination())
                    return false;

                dates = Browser.SelectList(Find.ById(TimeComboId));
                if (dates.Check())
                {
                    if (SomeLog != null)
                    {
                        var error = Browser.Span(Find.ById(ErrorLabelId));
                        var args = new MessageEventArgs
                        {
                            Message = error.Check() ? error.Text : Consult.ConsultWorker_RunLifeCycle_There_are_no_empty_dates
                        };
                        SomeLog(this, args);
                    }
                    continue;
                }

                if (SomeMessage != null)
                {
                    SomeMessage(this,
                        new MessageEventArgs { Message = Consult.ConsultWorker_RunLifeCycle_Work_with_ + selectedDate + " " + selectedTime });
                }

                return true;
            }
        }

        private bool ProveTermination()
        {
            Application.DoEvents();
            return !_isStopped;
        }

        private bool InitCountry()
        {
            return InitValue(CountryComboId, Consult.ConsultWorker_InitCity_Country, CountryName);
        }

        private bool InitCity()
        {
            return InitValue(CityComboId, Consult.ConsultWorker_InitCity_City, CityName);
        }

        private bool InitType()
        {
            return InitValue(TypeComboId, Consult.ConsultWorker_InitType_Type, TypeName);
        }


        private bool InitOffice(bool withEmpty = false)
        {
            return InitValue(OfficeComboId, Consult.ConsultWorker_InitOffice_Office, OfficeName, withEmpty);
        }

        private bool InitLink()
        {
            if (!ProveTermination())
                return false;
            var lnk = Browser.Link(Find.ById(LinkId));

            if (!lnk.Check())
            {
                if (SomeMessage != null)
                {
                    var arg = new MessageEventArgs
                    {
                        Message =
                            string.Format(Consult.ConsultWorker_InitLink_Link_on_current_action_does_not_found)
                    };
                    SomeMessage(this, arg);
                    if (arg.IsTerminate)
                    {
                        {
                            _isStopped = true;
                        }
                    }
                }
                return false;
            }

            lnk.Click();

            Browser.WaitForAsyncPostBackToComplete();
            return true;
        }

        private bool InitValue(string controlId, string nameForMessage, string textToSelect, bool withEmpty = false)
        {
            if (!ProveTermination())
                return false;
            var lan = Browser.SelectList(Find.ById(controlId));

            if (withEmpty && lan.Check())
            {
                lan.SelectByValue("-1");
                Browser.WaitForAsyncPostBackToComplete();
            }

            string val = null;
            if (lan.Check())
            {
                val = (from option in lan.Options
                       where option.Text != null
                       where option.Text.Trim().ToUpper() == textToSelect.Trim().ToUpper()
                       select option.Value).FirstOrDefault();
            }

            if (val == null && SomeMessage != null)
            {
                var arg = new MessageEventArgs
                {
                    Message =
                        string.Format(
                            Consult.ConsultWorker_InitValue__1__with_name___0___does_not_exists__Please__choose_another__2_,
                            textToSelect, nameForMessage.Substring(0, 1).ToUpper() + nameForMessage.Substring(1), nameForMessage)
                };
                SomeMessage(this, arg);
                if (arg.IsTerminate)
                {
                    {
                        _isStopped = true;
                        return false;
                    }
                }
            }

            lan.Select(textToSelect);
            Browser.WaitForAsyncPostBackToComplete();
            return true;
        }

        private bool ProveOnLanguage()
        {
            if (!ProveTermination())
                return false;
            var lan = Browser.SelectList(Find.ById(LanguageComboId));
            if (!lan.Check() || lan.Options.FirstOrDefault(o => o.Value == LanguageId.ToString() && o.Selected) != null)
                return true;
            lan.SelectByValue(LanguageId.ToString());
            var apply = Browser.Button(Find.ById(LanguageChangeButton));
            if (apply.Check())
                apply.Click();
            return true;
        }

        private bool ProveOnCaptcha()
        {
            if (!ProveTermination())
                return false;
            var captcha = Browser.Image(Find.ById(CaptchaImageId));
            var answer = Browser.TextField(Find.ById(CaptchaTextId));
            while (captcha.Check() && answer.Check())
            {
                if (!ProveTermination())
                    return false;
                var img = captcha.GetPicture(Browser);
                var arg = new CaptchaEventArgs { Image = img };
                if (NeedCapthaRecognize != null)
                {
                    NeedCapthaRecognize(this, arg);
                    if (!ProveTermination())
                        return false;
                }
                if (string.IsNullOrWhiteSpace(arg.Result))
                {
                    MessageBox.Show(Consult.ConsultWorker_ProveOnCaptcha_You_have_to_set_answer_manually);
                }
                else
                {
                    answer = Browser.TextField(Find.ById(CaptchaTextId));
                    if (answer.Check())
                        answer.Value = arg.Result;
                }
                var next = Browser.Button(Find.ById(NextButtonId));
                if (next.Check())
                    next.Click();
                Browser.WaitForAsyncPostBackToComplete();
                captcha = Browser.Image(Find.ById(CaptchaImageId));
                answer = Browser.TextField(Find.ById(CaptchaTextId));
            }
            return true;
        }

        public void Terminate()
        {
            _isStopped = true;
            //CloseBrowser(_ie);
            //CloseBrowser(_ff);
            //CloseBrowser(_ch);
        }

        public void Dispose()
        {
            _isStopped = true;
            CloseBrowser(_ie);
            CloseBrowser(_ff);
            //CloseBrowser(_ch);
        }
    }

    public static class BrowserExtensions
    {
        const string JS =
@"var div = document.images[{0}];
  div.contentEditable ='true';
  var controlRange;
  if(document.body.createControlRange)
  {{
       controlRange = document.body.createControlRange();
       controlRange.addElement(div);
       controlRange.execCommand('Copy');
   }}
   div.contentEditable = 'false';";

        private const string EVAL_FOR_BROWSER = "Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack();";

        public static bool Check(this Element el)
        {
            return el != null && el.Exists;
        }

        public static void WaitForAsyncPostBackToComplete(this Browser browser)
        {
            if (!(browser is IE)) return;
            var isInPostback = true;
            while (isInPostback)
            {
                Application.DoEvents();
                try
                {
                    isInPostback = Convert.ToBoolean(browser.Eval(EVAL_FOR_BROWSER));
                }
                catch
                {
                    return;
                }
                if (isInPostback)
                {
                    Thread.Sleep(200);
                }
            }
            return;
        }

        public static System.Drawing.Image GetPicture(this Image image, Browser browser)
        {
            if (image == null || !image.Exists || string.IsNullOrWhiteSpace(image.Src))
                return null;
            var cnt = -1;
            foreach (var image1 in browser.Images)
            {
                cnt++;
                if (image1 != null && image1.Exists && !string.IsNullOrWhiteSpace(image1.Src) && image1.Src.ToLower() == image.Src.ToLower())
                    break;
            }
            var script = string.Format(JS, cnt);
            browser.RunScript(script);

            var data = Clipboard.GetDataObject();
            if (data == null)
                return null;
            if (data.GetDataPresent(DataFormats.Bitmap))
            {
                var img = data.GetData(DataFormats.Bitmap, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.Dib))
            {
                var img = data.GetData(DataFormats.Dib, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.EnhancedMetafile))
            {
                var img = data.GetData(DataFormats.EnhancedMetafile, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.MetafilePict))
            {
                var img = data.GetData(DataFormats.MetafilePict, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.Tiff))
            {
                var img = data.GetData(DataFormats.Tiff, true);
                return img as System.Drawing.Image;
            }
            if (data.GetDataPresent(DataFormats.Serializable))
            {
                var img = data.GetData(DataFormats.Serializable, true);
                return img as System.Drawing.Image;
            }
            return null;
        }

    }
}
